<?php

namespace tmcore\services\addons\module;

use tmcore\exceptions\AddonsException;
use tmcore\services\addons\AddonApiService;
use tmcore\services\addons\BaseService;
use tmcore\services\addons\ModelService;
use think\Exception;
use tmcore\utils\File;

class Update extends BaseService
{
    protected string $addon_key;

    protected string $addon_cache;

    public function __construct(string $addon = '')
    {
        parent::__construct();
        $this->addon_key = $addon;
        $this->addon_cache = 'addon_'. $this->addon_key. '_install';
    }

    /**
     * 安装前检验
     * @param $params
     * @param $is_local
     * @return true
     * @throws AddonsException
     * @throws Exception
     */
    protected function check($params, $is_local)
    {
        if (!is_dir($this->sys_path . 'admin' . DIRECTORY_SEPARATOR)) throw new Exception('后台 admin 目录不存在');
        if (!is_dir($this->sys_path . 'uniapp' . DIRECTORY_SEPARATOR)) throw new Exception('手机端 uniapp 目录不存在');
        if (!is_dir($this->sys_path . 'pc' . DIRECTORY_SEPARATOR)) throw new Exception('电脑端 pc 目录不存在');

        $info = (new ModelService)->getInfoByKey($this->addon_key);
        if(!$info) throw new AddonsException('插件未安装');
        if($is_local &&  empty($info['is_local'])) throw new AddonsException('插件非本地插件');
        if(!(version_compare($params['version'], $info['version']) > 0)) throw new AddonsException('插件更新版本有误');

        // 检验目录
        $addons_admin_dir = $this->sys_path . 'admin/src/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addons_app_dir = $this->sys_path . 'uniapp/src/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addons_pc_dir = $this->sys_path . 'pc/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addons_server_dir = $this->addon_path . $this->addon_key . DIRECTORY_SEPARATOR;
        $addons_resource_dir = $this->root_path . 'public/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;

        $dir_arr = [$addons_admin_dir, $addons_app_dir, $addons_pc_dir, $addons_server_dir, $addons_resource_dir];

        foreach ($dir_arr as $dir) {
            if(!is_dir($dir)){
                File::mk_dir($dir, 0755);
            } else {
                if(!File::is_write($dir)) throw new AddonsException( str_replace($this->sys_path, '', $dir) . ' 目录不可写');
            }
        }

        // 检验预下载插件文件
        $addons_download_dir = $this->download_path . $this->addon_key . DIRECTORY_SEPARATOR;
        if(!$addons_download_dir) throw new AddonsException('插件文件未下载');

        $addon_file = $addons_download_dir . 'server/Addon.php';
        if(!is_file($addon_file)) throw new AddonsException('插件类文件缺失');

        $info_file = $addons_download_dir . 'server/info.php';
        if(!is_file($info_file)) throw new AddonsException('插件配置文件缺失');

        // 核对版本信息
        $tempArr = (array) include $info_file;
        if(!(isset($params['version']) && $tempArr['version'] == $params['version'])) throw new AddonsException('插件版本不匹配');

        return true;
    }

    /**
     * 更新目录
     * @return true
     */
    protected function updateDir()
    {
        // 下载目录
        $download_admin_dir = $this->download_path . $this->addon_key . '/admin' . DIRECTORY_SEPARATOR;
        $download_app_dir = $this->download_path . $this->addon_key . '/uniapp' . DIRECTORY_SEPARATOR;
        $download_pc_dir = $this->download_path . $this->addon_key . '/pc' . DIRECTORY_SEPARATOR;
        $download_server_dir = $this->download_path . $this->addon_key . '/server' . DIRECTORY_SEPARATOR;
        $download_resource_dir = $this->download_path . $this->addon_key . '/resource' . DIRECTORY_SEPARATOR;

        // 转移目录
        $addon_admin_dir = $this->sys_path . 'admin/src/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addon_app_dir = $this->sys_path . 'uniapp/src/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addon_pc_dir = $this->sys_path . 'pc/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;
        $addon_server_dir = $this->addon_path . $this->addon_key . DIRECTORY_SEPARATOR;
        $addon_resource_dir = $this->root_path . 'public/addons/' . $this->addon_key . DIRECTORY_SEPARATOR;

        // 安装后台管理端
        if (file_exists($download_admin_dir)) {
            File::copy_dir($download_admin_dir, $addon_admin_dir);
        }

        // 安装手机端
        if (file_exists($download_app_dir)) {
            File::copy_dir($download_app_dir, $addon_app_dir);
        }

        // 安装电脑端
        if (file_exists($download_pc_dir)) {
            File::copy_dir($download_pc_dir, $addon_pc_dir);
        }

        // 安装服务端
        if (file_exists($download_server_dir)) {
            File::copy_dir($download_server_dir, $addon_server_dir);
        }

        // 安装资源文件
        if (file_exists($download_resource_dir)) {
            File::copy_dir($download_resource_dir, $addon_resource_dir);
        }

        return true;
    }

    /**
     * 更新依赖
     * @return true
     * @throws Exception
     */
    protected function updateDepend()
    {
        $Depend = (new Depend);

        // composer 依赖更新
        $sys_composer_content = $Depend->getSysComposerContent();
        $addon_composer_content = $Depend->getAddonComposerContent($this->addon_key);
        if($addon_composer_content) {
            if (isset($addon_composer_content['require'])) {
                $sys_composer_content['require'] = array_merge($sys_composer_content['require'], $addon_composer_content['require']);
            }
            if (isset($addon_composer_content['require-dev'])) {
                $sys_composer_content['require-dev'] = array_merge($sys_composer_content['require-dev'], $addon_composer_content['require-dev']);
            }
            $Depend->setSysComposerContent($sys_composer_content);
        }

        foreach (['admin', 'uniapp', 'pc'] as $item) {
            // npm 依赖更新
            $sys_npm_content = $Depend->getSysNpmContent($item);
            $addon_npm_content = $Depend->getAddonNpmContent($this->addon_key, $item);
            if($addon_npm_content) {
                if (isset($addon_npm_content['dependencies'])) {
                    $sys_npm_content['dependencies'] = array_merge($sys_npm_content['dependencies'], $addon_npm_content['dependencies']);
                }
                if (isset($addon_npm_content['devDependencies'])) {
                    $sys_npm_content['devDependencies'] = array_merge($sys_npm_content['devDependencies'], $addon_npm_content['devDependencies']);
                }
                $Depend->setSysNpmContent($sys_npm_content, $item);
            }
        }

        // pages.json 更新
        $sys_page_content = $Depend->getSysPageContent();
        $addon_page_content = $Depend->getAddonPageContent($this->addon_key);
        if($addon_page_content) {
            if (isset($addon_page_content['pages'])) {
                // 路径相同不操作
                foreach ($sys_page_content['pages'] as $key => $item) {
                    foreach ($addon_page_content['pages'] as $k => $addon_item) {
                        if(isset($addon_item['path']) && $item['path'] == $addon_item['path']) {
                            unset($addon_page_content['pages'][$k]);
                        }
                    }
                }
                // 路径不同合并
                foreach ($addon_page_content['pages'] as $k => $addon_item) {
                    if(isset($addon_item['path'])) {
                        $sys_page_content['pages'][] = $addon_item;
                    }
                }
                $Depend->setSysPageContent($sys_page_content);
            }
        }

        return true;
    }

    /**
     * 备份目录
     * @return true
     */
    protected function backupDir()
    {
        $backup_dir = $this->root_path . 'data/backup' . DIRECTORY_SEPARATOR;
        if(is_dir($backup_dir)) {
            File::del_dir($backup_dir);
        } else {
            File::mk_dir($backup_dir);
        }

        // 循环备份前端目录
        foreach (['admin', 'app', 'pc'] as $item) {
            $module_dir = $this->root_path . 'public/' . $item . DIRECTORY_SEPARATOR;
            if (is_dir($module_dir)) {
                if (!is_dir($backup_dir . $item)) File::mk_dir($backup_dir . $item, 0755);
                File::copy_dir($module_dir, $backup_dir . $item);
            }
        }

        $package_dir = $backup_dir . 'package' . DIRECTORY_SEPARATOR;
        if(!is_dir($package_dir)) File::mk_dir($package_dir, 0755);

        // 备份 composer package pages 原文件，只备份原始文件
        if(!file_exists($package_dir . 'composer.json')) {
            copy($this->root_path . 'composer.json', $package_dir . 'composer.json');
        }

        if(!file_exists($package_dir . 'admin_package.json')) {
            copy($this->sys_path . 'admin/package.json', $package_dir . 'admin_package.json');
        }

        if(!file_exists($package_dir . 'uniapp_package.json')) {
            copy($this->sys_path . 'uniapp/package.json', $package_dir . 'uniapp_package.json');
        }

        if(!file_exists($package_dir . 'pc_package.json')) {
            copy($this->sys_path . 'pc/package.json', $package_dir . 'pc_package.json');
        }

        if(!file_exists($package_dir . 'uniapp_pages.json')) {
            copy($this->sys_path . 'uniapp/src/pages.json', $package_dir. 'uniapp_pages.json');
        }

        return true;
    }

    /**
     * 安装sql
     * @return true
     */
    protected function executeSql()
    {
        (new Sql)->executeSql($this->download_path . $this->addon_key . '/server/sql/update.sql');
        return true;
    }

    /**
     * 安装异常
     * @return true
     */
    protected function installExceptionHandle()
    {
        $backup_dir = $this->root_path . 'data/backup' . DIRECTORY_SEPARATOR;

        // 还原 composer package pages 文件
        if(is_file($backup_dir . 'package/composer.json')) {
            copy($backup_dir . 'package/composer.json', $this->root_path . 'composer.json');
        }

        if(is_file($backup_dir . 'package/admin_package.json')) {
            copy($backup_dir . 'package/admin_package.json', $this->sys_path . 'admin/package.json');
        }

        if(is_file($backup_dir . 'package/uniapp_package.json')) {
            copy($backup_dir . 'package/uniapp_package.json', $this->sys_path . 'uniapp/package.json');
        }

        if(is_file($backup_dir . 'package/pc_package.json')) {
            copy($backup_dir . 'package/pc_package.json', $this->sys_path . 'pc/package.json');
        }

        if(is_file($backup_dir . 'package/uniapp_pages.json')) {
            copy($backup_dir . 'package/uniapp_pages.json', $this->sys_path . 'uniapp/src/pages.json');
        }

        // 删除备份文件
        if (is_dir($backup_dir)) File::del_dir($backup_dir);

        return true;
    }


    /**
     * 安装执行
     * @return true
     * @throws Exception
     */
    protected function updateExecute()
    {
        // 获取方法
        $class = 'addons\\' . $this->addon_key . '\\' . 'Addon';
        if(!class_exists($class)) throw new Exception('插件类加载失败');

        // 安装sql
        $this->executeSql();

        $addonClass = (new $class());

        // 执行update方法
        $addonClass->update();

        // 设置插件缓存
        $addonClass->getInfo();
        $addonClass->getConfig();
        $addonClass->getEvent();
        $addonClass->getCommand();

        return true;
    }


    /**
     * 更新插件配置信息
     * @param array $params
     * @param bool $is_local
     * @return void
     * @throws AddonsException
     * @throws Exception
     */
    public function updateAddonInfo($params, $is_local)
    {
        if($is_local) {
            $update_info = [
                'name' => $params['name'] ?? '',
                'thumb' => $params['thumb'] ?? '',
                'identify' => $this->addon_key,
                'version' => $params['version'],
            ];
            (new ModelService)->setAddonInfo($update_info);
        } else {
            $res = AddonApiService::get_addons_info($this->addon_key);
            if(isset($res['identify'])) {
                $res['version'] = $params['version'];
                (new ModelService)->setAddonInfo($res);
            } else {
                throw new Exception('获取插件信息失败');
            }
        }
    }

    /**
     * 更新插件
     * @param $params
     * @param $is_local
     * @return true
     * @throws AddonsException
     */
    public function main($params, $is_local)
    {
        // 更新前检验
        $this->check($params, $is_local);

        try {

            set_time_limit(0);

            // 更新目录
            $this->updateDir();

            // 备份目录
            $this->backupDir();

            // 更新执行
            $this->updateExecute();

            // 更新依赖
            $this->updateDepend();

            // 更新插件配置信息
            $this->updateAddonInfo($params, $is_local);

            return true;

        } catch (\Exception $e) {
            $this->installExceptionHandle();
            throw new AddonsException($e->getMessage());
        }
    }
}